package jee.boot.data.permission.execute;

import com.google.common.collect.Maps;
import jee.boot.common.basic.DataEntity;
import jee.boot.common.basic.interceptor.PreQueryInterceptor;
import jee.boot.common.utils.JeeBootContext;
import jee.boot.common.utils.MatcherCompilationHelper;
import jee.boot.common.utils.StringUtils;
import jee.boot.data.permission.config.AuthInterceptorThreadContext;
import jee.boot.entity.auth.*;
import jee.boot.entity.auth.AuthConstant;
import jee.boot.entity.system.Operator;
import jee.boot.entity.system.TreeModel;
import org.springframework.util.AntPathMatcher;

import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 对查询的请求进行拦截,填充权限条件
 */
public class PreQueryExecuteInterceptor extends BasicExecute implements PreQueryInterceptor {
    private static AntPathMatcher matcher = new AntPathMatcher();

    public PreQueryExecuteInterceptor() {
        super();
    }

    /**
     * 执行前置操作
     *
     * @param entity
     */
    @Override
    public void execute(DataEntity<?> entity) {
        if (!AuthInterceptorThreadContext.isAuthPermission()) {
            return;
        }
        User currentUser = getCurrentUser();
        HttpServletRequest httpServletRequest = getHttpServletRequest();
        String uri;
        if (httpServletRequest != null) {
            uri = httpServletRequest.getRequestURI();
        } else {
            uri = JeeBootContext.get(JeeBootContext.REFERER_URI);
        }
        // 如果没有request,并且没有设置上下文的环境变量,不做拦截
        if (StringUtils.isBlank(uri)) {
            return;
        }
        // 没有授权用户的不拦截
        if (currentUser == null) {
            return;
        }
//        List<AuthEntity> rowSetList = mergeRowSetPermission(currentUser.getRowSetList(), uri);
        List<AuthEntity> rowSetList = currentUser.getRowSetList();
        // 模型的数据权限拦截
        if (StandardModel.class.equals(entity.getClass())) {
            StandardModel standardModel = (StandardModel) entity;
            // 配置Column权限
            configColumnPermission(currentUser, standardModel);
            // 配置rowSet权限
            configRowSetPermission(currentUser, standardModel);
            // 配置CellSet权限
            configCellSetPermission(currentUser, standardModel);
        }

        // 标准请求的数据权限拦截
        String sql = authority2Sql(currentUser, rowSetList, entity);
        entity.configAdditionalConditionsSql(sql);


    }


    /**
     * 权限规则转换为规则sql片段
     *
     * @param user       当前用户
     * @param rowSetList rowSet权限列表
     * @param entity     入参
     * @return
     */
    public String authority2Sql(User user, List<AuthEntity> rowSetList, DataEntity<?> entity) {
        StringBuilder sql = new StringBuilder();
        boolean hasAuth = false;
        boolean firstRow = true;
        // 业务实体数据对应的权限模型
        ModelData modelDataByEntity = getModelDataByEntity(user, entity);
        if (modelDataByEntity != null) {
            List<AuthEntity> authRowSetList = getAuthRowSetList(rowSetList, modelDataByEntity);
            // 如果存在Rowset的权限规则,则进行数据查询的拦截
            if (authRowSetList != null && !authRowSetList.isEmpty()) {
                for (AuthEntity authEntity : authRowSetList) {
                    RowSet rowSet = (RowSet) authEntity.getEntity();
                    List<RowSetItem> itemList = rowSet.getItemList();
                    boolean firstItem = true;
                    StringBuilder subSql = new StringBuilder();
                    for (RowSetItem rowSetItem : itemList) {
                        Column dim = rowSetItem.getDim();
                        Operator operator = rowSetItem.getOperator();
                        if (StringUtils.isBlank(operator.getId())) {
                            //没有operator,属于无效设置,忽略
                            continue;
                        }
                        if (firstItem) {
                            firstItem = false;
                        } else {
                            subSql.append("AND");
                        }
                        String dataDomainType = dim.getDataDomainType();
                        Map<String, Object> f = Maps.newHashMap();
                        switch (dataDomainType) {
                            // 树节点
                            case AuthConstant.SYSTEM_DATA_DOMAIN_TYPE_TREE_NODE:
                                f = treeDataExecute(entity, modelDataByEntity, dim, operator, rowSetItem, user);
                                break;
                            // 字典取值
                            case AuthConstant.SYSTEM_DATA_DOMAIN_TYPE_DICTIONARY:
                                // 主数据
                            case AuthConstant.SYSTEM_DATA_DOMAIN_TYPE_MASTER_DATA:
                                // 枚举字符串
                            case AuthConstant.SYSTEM_DATA_DOMAIN_TYPE_MENU_STRING:
                                //字典,主数据,枚举字符串,都采用普通的处理方式,包括 =,>,<,like,等基础操作
                                f = basicExecute(modelDataByEntity, dim, operator, rowSetItem, user);
                                break;
                        }
                        subSql.append(entity.spliceSql(f));
                        hasAuth = true;
                    }
                    if (StringUtils.isNotBlank(subSql.toString())) {
                        if (firstRow) {
                            sql.append("(");
                            firstRow = false;
                        } else {
                            sql.append("OR(");
                        }
                        sql.append(subSql.toString());
                        sql.append(")");
                    }

                }
            }
        }
        if (!hasAuth) {// 没有配置权限
            return "";
        }
        return "(" + sql.toString() + ")";
    }

    /**
     * 获取当前模型用于过滤数据的权限列表
     * @param rowSetList
     * @param modelData
     * @return
     */
    public List<AuthEntity> getAuthRowSetList(List<AuthEntity> rowSetList, ModelData modelData) {
        // 获取到父类模型
        ModelData superModelData = modelData.getSuperModelData();
        // 需要应用到entity中的rowSet权限规则列表,默认指向自己的模型类
        List<AuthEntity> authRowSetList = rowSetList.stream().filter(rowSet ->
                StringUtils.equals(rowSet.getModelData().getId(), modelData.getId())
        ).collect(Collectors.toList());
        // 如果自己的模型类没有设置Rowset权限,则需要读取父类的配置
        if (authRowSetList == null || authRowSetList.isEmpty()) {
            if (superModelData != null && StringUtils.isNotBlank(superModelData.getId())) {
                // 如果父类模型存在,则加载父类模型的Rowset权限规则
                authRowSetList = rowSetList.stream().filter(rowSet ->
                        StringUtils.equals(rowSet.getModelData().getId(), superModelData.getId())
                ).collect(Collectors.toList());
            }
        }
        return authRowSetList;
    }

    /**
     * 根据实体数据查询其对应的业务权限模型
     *
     * @param user
     * @param entity
     * @return
     */
    private ModelData getModelDataByEntity(User user, DataEntity entity) {
        List<AuthEntity> modelDataList = user.getModelDataList();
        if (modelDataList != null && !modelDataList.isEmpty()) {
            for (AuthEntity authEntity : modelDataList) {
                ModelData modelData = (ModelData) authEntity.getEntity();
                if (equalModelIgnoreSchema(modelData, entity)) {
                    return modelData;
                }
            }
        }
        return null;
    }


    /**
     * @param entity
     * @param modelData
     * @param dim
     * @param operator
     * @param rowSetItem
     * @param user
     * @return
     */
    private Map<String, Object> treeDataExecute(DataEntity<?> entity, ModelData modelData, Column dim, Operator operator, RowSetItem rowSetItem, User user) {
        Map<String, Object> f = new HashMap<>();
        f.put("operate", "built-in");//内置的sql片段,不经过sql的防注入转义
        f.put("right", null);
        // 设置别名
        String alias = "";
        if (!StringUtils.equals("a", modelData.getTableAlias())) {
            alias = modelData.getTableAlias() + ".";
        }
        String treeModelId = dim.getDataDomain();
        TreeModel treeModel = remoteEndpoint.executePost(projectConfig.getOpenSystemServer(),"/treeModel/get",new TreeModel(treeModelId),TreeModel.class);
        if (treeModel != null) {
            String left = alias + entity.getColumnByFieldName(dim.getCode()) + " in ";
            left += "(" + tableTreeSql(user, rowSetItem, treeModel) + ")";
            f.put("left", left);
        }
        return f;
    }


    /**
     * 基本操作处理方案,返回一个filter
     *
     * @param modelData
     * @param dim
     * @param operator
     * @param rowSetItem
     * @param user
     */
    private Map<String, Object> basicExecute(ModelData modelData, Column dim, Operator operator, RowSetItem rowSetItem, User user) {
        Map<String, Object> f = new HashMap<>();
        // 设置别名
        String alias = "";
        if (!StringUtils.equals("a", modelData.getTableAlias())) {
            alias = modelData.getTableAlias() + ".";
        }
        f.put("left", alias + dim.getCode());
        f.put("operate", operator.getTechName());
        f.put("right", MatcherCompilationHelper.simpleCompilation(user, rowSetItem.getFormula()));
        return f;
    }

}
